/* 装饰本身就是一个函数
*  Demo 函数会在Person 类定义的时候执行
*  参数说明：
*     target: 被装饰的类
*  返回值:如果返回的是一个新类，则替换掉原来的类
*
*
*
* */
function Demo(target: Function) {
    console.log("Demo")
    console.log(target)

}


//  构造类型:
type Constructor = new (...args: any[]) => {}

function LogTime<T extends Constructor>(target: T) {
    console.log("LogTime")
    return class extends target {
        createdTime: Date

        constructor(...args: any) {
            super(args);
            this.createdTime = new Date()
        }

        getTime() {
            return `该对象创建的时间 ${this.createdTime}`
        }
    }
}

@Demo
@LogTime
@LogInfo(3)
class Person {
    name: String

    constructor(name: String) {
        this.name = name;
    }
}

const p = new Person("张三")
console.log(p)


// 装饰器的工厂函数 (可以传递参数) 包含装饰器的装饰器
function LogInfo(count: number) { // 外层的叫装饰器功能
    console.log("LogInfo 工厂")
    return function (target: Function) { // 内层的叫装饰器
        console.log("LogInfo")
        target.prototype.introduce = function () {
            for (let i = 0; i < count; i++) {
                console.log(`我叫${this.name},今年${count}岁`)
            }

        }
    }
}


// 装饰器的组合
/*
* @Demo
  @LogTime    执行顺序 先工厂（从上到下）->装饰器(从下到上)
 @LogInfo(3)
*
* */


//-----------------------------------------以上是类装饰器------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// 属性装饰器  属性遮蔽问题

/**
 *
 * @param target 类的原型对象 或者 静态变量的 类
 * @param propertyKey 属性名
 * @constructor
 */
function Log(target: object, propertyKey: string) {

    console.log("属性代理 log", target, propertyKey)
}

/**
 * 多个实例会共享一个值
 * @param target
 * @param propertyKey
 * @constructor
 */
function State(target: object, propertyKey: string) {
    let value: any
    Object.defineProperty(target, propertyKey, {
        get(): any {
            return value
        },
        set(v: any) {
            console.log("setter")
            value = v
        }
    })
}

function StateOptimize(target: object, propertyKey: string) {
    let key: string = `__${propertyKey}`
    Object.defineProperty(target, propertyKey, {
        get(): any {
            return this[key]
        },
        set(v: any) {
            console.log("setter")
            this[key] = v
        }
    })
}

class Person1 {
    @StateOptimize
    name: String
    @Log
    static school: String

    constructor(name: String) {
        console.log("构造函数")
        this.name = name;
    }
}

let p1 = new Person1("张三")

p1.name = "张三"

//-----------------------------------------以上是属性饰器------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

function MethodDector(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("方法装饰器")
    console.log(target, propertyKey, descriptor)
    let originalMethod = descriptor.value
    descriptor.value = function (...args: any[]) {
        console.log("方法执行开始")
        const result = originalMethod.call(this, ...args) // 这里要... 一个一个传进去 originalMethod.apply(this,args)
        console.log("方法执行结束")
        return result
    }
}

// 方法装饰器
class Person2 {

    name: String

    static school: String

    constructor(name: String) {
        console.log("构造函数")
        this.name = name;
    }

    @MethodDector
    speak(params:string,age:number) {
        console.log("speak",params,age)
    }

    @MethodDector
    static speakStatic() {
        console.log("speakStatic")
    }
}

let p2 = new Person2("hahh")
p2.speak("I love you",18)


//-----------------------------------------以上是方法饰器------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
